﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using ContactManagement.AppCode;
using EFDataModel;
using EFDataModel.Enums;

namespace ContactManagement
{
    /// <summary>
    /// Athorization Attribut, was auswertet ob die UserRolle die übergeben wurde auf den aktellen View oder die Action zugreifen darf,
    /// dafür muss das Attribut gesetzt werden.
    /// Quellen für Filter und Attribute in ASP.NET MVC
    /// http://msdn.microsoft.com/en-us/library/gg416513%28VS.98%29.aspx
    /// http://msdn.microsoft.com/en-us/library/dd381609%28v=VS.98%29.aspx
    /// http://geekswithblogs.net/brians/archive/2010/07/08/implementing-a-custom-asp.net-mvc-authorization-filter.aspx
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        #region Member
        private EControllers Controller { get; set; }
        private string ActionName { get; set; }
        private AuthorizationContext CurrentContext { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Es muss nichts übergeben werden, da controller und action direkt hier ausgelesen werden können
        /// </summary>
        public CustomAuthorizeAttribute()
        {
        }
        #endregion

        #region Public Functions
        /// <summary>
        /// Funktion die ausgeführt wird, wenn das Attribut "genutzt" wird, das Authorisation Attribut wird immer als erstes Attribut ausgeführt.
        /// </summary>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            //Der Filtercontext wird benötigt um auf die RequestDaten zuzugreifen, z.b. auf die UserId die zugegriffen werden soll.
            CurrentContext = filterContext;

            //Setzen des aktuellen Actionnamen, der aufgerufen wird.
            ActionName = filterContext.ActionDescriptor.ActionName;
            //Den aktuellen Controller ermitteln, dieser ist in RouteData enthalten, des aktuellen filterContext.
            Controller = (EControllers)Enum.Parse(typeof(EControllers), filterContext.RouteData.GetRequiredString(WebConstants.ControllerString));

            //Wenn kein User eingeloggt ist, dann hat er auch keinen Zugriff.
            if (CurrentSession.GetCurrentUser() == null)
            {
                //The AutorizeAttribute has nothing to do with the redirection to the Login page. The AutorizeAttribute only checks for the user rights to access the Action and sets HTTP Status Code 401 Unauthorized on the respons
                //Wenn der User nicht eingeloggt ist, auf die Loginseite verweisen
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else
            {
                //Prüfen der passenden Rechte für die einzelnen Actions/Methoden
                //Wenn der User nicht für den View/Action authentifiziert ist, dann auf die Loginseite verweisen.
                if (!CheckRights())
                {
                    //Auf die Startseite verweisen, wenn der User keinen Zugriff auf den Kontent hat, den er angefordert hat.
                    filterContext.Result = new RedirectResult(WebConstants.UrlWebsite);
                }
            }

            //Wenn alles i.o. ist "nichts" unternehmen und einfach beim Aufbau der Seite weitermachen.
        }
        #endregion

        #region Private Functions
        /// <summary>
        /// Prüfen um welchen Controller es sich handelt und die passende Sicherheitsprüfung vornehmen
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckRights()
        {
            //Prüfen welcher Controller das Attribut aufgerufen hat und dann schauen welche Action aufgerufen wurde.
            switch (Controller)
            {
                case EControllers.Account:
                    return CheckAccountRights();

                case EControllers.User:
                    return CheckUserRights();

                case EControllers.Home:
                    return CheckHomeRights();

                case EControllers.Administration:
                    return CheckAdministrationRights();

                case EControllers.DynamicData:
                    return WebRights.Right().Check(EUserRights.CreateDynamicDataTypes);
            }

            return false;
        }

        /// <summary>
        /// Prüfen der UserActions
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckUserRights()
        {
            //den passenden Enum Wert ermitteln
            EActionUser eActionUser = (EActionUser)Enum.Parse(typeof(EActionUser), ActionName);

            switch (eActionUser)
            {
                //Bei den Details/Edit muss geprüft werden ob der aktuelle User auch der ist der angezeigt werden soll oder es sich um einen Admin handelt.
                //Sein eigenes Passwort kann der User auch jederzeit ändern.
                case EActionUser.Details:
                case EActionUser.ChangePassword:
                case EActionUser.ChangePasswordSuccess:
                case EActionUser.Edit:
                    //Der aktuelle User entspricht dem der eingeloggt ist.
                    if (CheckUrlIDIsCurrentUser()) return true;

                    //Der aktuelle User ist ein Admin oder Superuser und der darf auf jeden User zugreifen
                    if (CurrentSession.GetCurrentUser().UserType == EUserTypes.SuperUser || CurrentSession.GetCurrentUser().UserType == EUserTypes.Admin)
                    {
                        return true;
                    }
                    break;

                case EActionUser.UserAdd:
                    //Wenn wir user Create Rights geben müssen wir auch UserEdit Rights geben, sonst kann der Nutzer nicht angelegt werden.
                    return WebRights.Right().Check(EUserRights.UserCreate);

                case EActionUser.UserDetails:
                    return WebRights.Right().Check(EUserRights.UserDetails);

                case EActionUser.UserEdit:
                    return WebRights.Right().Check(EUserRights.UserEdit);

                case EActionUser.UserEditUserGroups:
                case EActionUser.UserEditUserGroupsAdd:
                case EActionUser.UserEditUserGroupsDelete:
                    return WebRights.Right().Check(EUserRights.SetUserGroups);

                case EActionUser.UserEditUserRights:
                case EActionUser.UserEditUserRightsAdd:
                case EActionUser.UserEditUserRightsDelete:
                case EActionUser.UserEditUserRightsDeleteComplete:
                    return WebRights.Right().Check(EUserRights.SetUserRights);

                //Es handelt sich nur um das Neue Versenden oder Erstellen von Passwörtern anderer Nutzer, 
                //Sein eigenen Passwort kann man immer noch selbst bearbeiten/ändern
                case EActionUser.UserSendNewPassword:
                    return WebRights.Right().Check(EUserRights.SetUserPassword);

                case EActionUser.UserList:
                    //Wenn er das UserModul sehen darf, dann darf er auch alle Nutzer sehen
                    return WebRights.Right().Check(EUserRights.UserModule);
            }

            //Alle Fälle die nicht abgedeckt sind, hat der User keine Zugriffe.
            return false;
        }

        /// <summary>
        /// Die AccountActions für den 
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckAccountRights()
        {
            //Im Account Bereich darf jeder zugreifen, da sich der User hier registriert.
            return true;
        }

        /// <summary>
        /// Prüfen der HomeActions
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckHomeRights()
        {
            //den passenden Enum Wert ermitteln
            EActionHome eActionHome = (EActionHome)Enum.Parse(typeof(EActionHome), ActionName);

            switch (eActionHome)
            {
                case EActionHome.Index:
                    break;
            }

            //Auf alle restlichen Bereiche im Homebereich darf zugegriffen werden
            return true;
        }

        /// <summary>
        /// Prüfen ob der User auch auf den Adminbereich zugreifen darf, dies darf nur der SuperUser oder admin
        /// </summary>
        private bool CheckAdministrationRights()
        {
            //Nur der SuperUser oder Admin darf auf diesen Bereich zugriff haben.
            if (CurrentSession.GetCurrentUser().IsAdminOrSuperUser())
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Prüft mit Hilfe des "AuthorizationContext" der die aktuelle URL enthält ob die ID in der URL mit der aktuell eingeloggten UserId übereinstimmt.
        /// </summary>
        /// <returns>TRUE->IDs stimmen überein | FALSE->IDs stimmen nicht überein</returns>
        private bool CheckUrlIDIsCurrentUser()
        {
            try
            {
                //Aus der URL den Parameter mit der id ermitteln
                int userId = int.Parse(CurrentContext.RouteData.GetRequiredString(WebConstants.IdString));

                //Den Url Parameter dann mit dem aktuell eingeloggten User vergleichen.
                if (CurrentSession.GetCurrentUser().ID == userId)
                {
                    return true;
                }
            }
            catch (Exception) { }

            return false;
        }
        #endregion
    }
}